/* eslint-disable no-unused-vars */
import CodeMirror from "codemirror";
import { generateMap } from "@triascloud/utils";

const REG_BRACKET = /[[\],()]/;
const REG_OPERATOR = /[+\-*/=<>!&|]/;
const REG_KEYWORD = /[{}]/;

const matchString = (stream, quote) => {
  let result = false;
  let current;
  while ((current = stream.next()) != null) {
    if (current === quote && !result) {
      return false;
    }
    result = !result && current === "\\";
  }
  return result;
};

function normalizeKeywords(keywords) {
  return (keywords || []).map((keyword) =>
    typeof keyword === "string"
      ? {
          text: keyword,
          value: keyword,
          cursorOffset: 0,
        }
      : keyword
  );
}

/** @type { (options: object) => import('codemirror').Mode['token'] } */
const tokenGenerator = (options, c) => {
  console.log('token的第二个参数');
  console.log(c);
  const keywordMap = generateMap(
    normalizeKeywords(options.keywords),
    'text',
    () => true,
  );
  return (stream) => {
    if (stream.eatSpace()) return null;
    const str = stream.next();
    if (str === '"' || str === `'`) {
      matchString(stream, str);
      return 'string';
    }
    if (REG_BRACKET.test(str)) {
      return 'bracket';
    }
    if (REG_OPERATOR.test(str)) {
      return 'operator';
    }
    if (/\d/.test(str)) {
      stream.eatWhile(/[\d.]/);
      return 'number';
    }
    stream.eatWhile(/[\w]/);
    const current = stream.current();
    if (current === 'true' || current === 'false') {
      return 'atom';
    }
    // console.log(keywordMap[current], current);
    if (keywordMap[current]) {
      return 'keyword';
    }
  };
};

/** @name 变量模式 */
export const MODE = "variable";

// CodeMirror.defineMode(MODE, options => ({
//   token: tokenGenerator(options),
// }));

function re() {
  const ae = /^{{\s*([^ }]+)\s*[^}]*\s*}}/
    , B = /^{%\s*([^ }]+)\s*[^%]*\s*%}/
    , G = /^{#\s*[^#]+\s*#}/;
  let Y = 1;
  return {
      startState() {
          return {
              inRaw: !1
          }
      },
      token(Q, be) {
          let X;
          if (Y *= -1,
          X = Q.match(B, !0),
          X) {
              const i = X[1];
              if (be.inRaw && i === "endraw")
                  be.inRaw = !1;
              else if (!be.inRaw && i === "raw")
                  be.inRaw = !0;
              else if (be.inRaw)
                  return null;
              return "nunjucks-tag ".concat(Y)
          }
          if (!be.inRaw && (X = Q.match(ae, !0),
          X))
              return "nunjucks-variable ".concat(Y);
          if (!be.inRaw && (X = Q.match(G, !0),
          X))
              return "nunjucks-comment ".concat(Y);
          for (; Q.next() != null && !(Q.match(ae, !1) || Q.match(B, !1) || Q.match(G, !1)); )
              ;
          return null
      }
  }
}

CodeMirror.defineMode(MODE, (ae,B)=>{
  const G = CodeMirror.getMode(ae, B.baseMode || "text/plain");
  const Y = re();
  return CodeMirror.overlayMode(G, Y, !1)
}
)

CodeMirror.registerHelper("hint", MODE, (cm) => {
  const cursor = cm.getCursor();
  const token = cm.getTokenAt(cursor);
  if (!token.string) return;
  const searchKey = token.string;
  const keywords = normalizeKeywords(cm.options.keywords);
  const list = keywords
    .filter((keyword) => keyword.text.startsWith(searchKey))
    .map((keyword) => ({
      text: keyword.text,
      hint: (cm, data) => {
        cm.replaceRange(keyword.value, data.from, data.to, "complete");
        if (!keyword.cursorOffset) return;
        const cursor = cm.getCursor();
        cm.setCursor({
          line: cursor.line,
          ch: cursor.ch + keyword.cursorOffset,
        });
        cm.focus();
      },
    }));
  if (!list.length) return null;
  return {
    _handlers: {
      select: [(...args) => CodeMirror.signal(cm, "hint-select", ...args)],
    },
    list,
    from: CodeMirror.Pos(cursor.line, token.start),
    to: CodeMirror.Pos(cursor.line, cursor.ch),
  };
});

CodeMirror.defineMIME("text/x-variable", MODE);
